// 静态生命周期  'static 意味着能和程序活得一样久，例如字符串字面量和特征对象

// 'static 在 Rust 中是相当常见的，例如字符串字面值就具有 'static 生命周期:

// &'static 和 T: 'static
/*
&'static
&'static 对于生命周期有着非常强的要求：一个引用必须要活得跟剩下的程序一样久，才能被标注为 &'static。
&'static 生命周期针对的仅仅是引用，而不是持有该引用的变量，对于变量来说，还是要遵循相应的作用域规则 :
*/

#[cfg(test)]
mod static_at {
    use std::{slice::from_raw_parts, str::from_utf8_unchecked};

    fn get_memory_location() -> (usize, usize) {
        // “Hello World” 是字符串字面量，因此它的生命周期是 `'static`.
        // 但持有它的变量 `string` 的生命周期就不一样了，它完全取决于变量作用域，对于该例子来说，也就是当前的函数范围
        let string = "Hello World!";
        let pointer = string.as_ptr() as usize;
        let length = string.len();
        (pointer, length)
        // `string` 在这里被 drop 释放
        // 虽然变量被释放，无法再被访问，但是数据依然还会继续存活
    }

    fn get_str_at_location(pointer: usize, length: usize) -> &'static str {
        // 使用裸指针需要 `unsafe{}` 语句块
        unsafe { from_utf8_unchecked(from_raw_parts(pointer as *const u8, length)) }
    }

    #[test]
    fn main() {
        // &'static 的->引用 确实可以和程序活得一样久，因为我们通过 get_str_at_location 函数直接取到了对应的字符串
        // 持有 &'static 引用->的->变量，它的生命周期受到作用域的限制，大家务必不要搞混了

        let (pointer, length) = get_memory_location();
        let message = get_str_at_location(pointer, length);
        println!(
            "The {} bytes at 0x{:X} stored: {}",
            length, pointer, message
        );
        // 如果大家想知道为何处理裸指针需要 `unsafe`，可以试着反注释以下代码
        // let message = get_str_at_location(1000, 10);
    }
}

// T: 'static
// 首先，在以下两种情况下，T: 'static 与 &'static 有相同的约束：T 必须活得和程序一样久。
#[cfg(test)]
mod static_t {
    use std::fmt::{Debug, Display};

    fn print_it<T: Debug + 'static>(input: T) {
        println!("'static value passed in is: {:?}", input);
    }

    fn print_it1(input: impl Debug + 'static) {
        println!("'static value passed in is: {:?}", input);
    }

    //原因在于我们约束的是 T，但是使用的却是它的引用 &T
    // 根本没有直接使用 T，因此编译器就没有去检查 T 的生命周期约束！它只要确保 &T 的生命周期符合规则即可
    fn print_it_ok<T: Debug + 'static>(input: &T) {
        println!("'static value passed in is: {:?}", input);
    }

    #[test]
    fn main() {
        let i = 5;
        print_it_ok(&i);
        // print_it(&i);
        // print_it1(&i);
    }

    #[test]
    fn main_() {
        let r1;
        let r2;
        {
            static STATIC_EXAMPLE: i32 = 42;
            r1 = &STATIC_EXAMPLE;
            let x = "&'static str";
            r2 = x;
            // r1 和 r2 持有的数据都是 'static 的，因此在花括号结束后，并不会被释放
        }

        println!("&'static i32: {}", r1); // -> 42
        println!("&'static str: {}", r2); // -> &'static str

        let r3: &str = "";

        {
            let s1 = "String".to_string();

            // s1 虽然没有 'static 生命周期，但是它依然可以满足 T: 'static 的约束
            // 充分说明这个约束是多么的弱。。
            static_bound(&s1);

            // s1 是 String 类型，没有 'static 的生命周期，因此下面代码会报错
            // r3 = &s1;

            // s1 在这里被 drop
        }
        println!("{}", r3);
    }

    fn static_bound<T: Display + 'static>(t: &T) {
        println!("{}", t);
    }

    #[test]
    fn asd() {
        {
            //static_string 生命周期是 'static，但是持有它的引用并不是，它的作用域在内部花括号 }
            let static_string = "I'm in read-only memory";
            println!("static_string: {}", static_string);

            // 当 `static_string` 超出作用域时，该引用不能再被使用，但是数据依然会存在于 binary 所占用的内存中
        }
        // println!("static_string: {}", static_string);
        // println!("static_string_at: {}", static_string_at);
    }
}

/*
    如果你需要添加 &'static 来让代码工作，那很可能是设计上出问题了
    如果你希望满足和取悦编译器，那就使用 T: 'static，很多时候它都能解决问题
*/
